<?php

namespace App\Admin\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\SoftDeletes;
use Throwable;

class Config extends Model
{
    use SoftDeletes;
    
    const CACHE_KEY = 'admin_config';
    const CONFIG_KEY = 'admin';

    protected $fillable = [
        'category_id', 
        'type', 
        'name', 
        'slug', 
        'desc', 
        'options', 
        'value', 
        'order',
        'validation_rules',
        'form_disabled',
    ];

    protected $casts = [
        'category_id' => 'integer',
        'options' => \App\Admin\Casts\Json::class,
        'value' => 'string',
        'form_disabled' => 'bool',
    ];

     // 重写 boot 方法来设置动态的 casts
     protected static function boot()
     {
        parent::boot();
        // 在这里，我们使用 'retrieved' 事件，当模型从数据库中检索出来时触发
        static::retrieved(function ($model) {
            $type = request()->input('type') ?? $model->type;
            $options = request()->input('options') ?? $model->options;
            $model->casts['value'] = SystemModel::typeToCasts($type, $options);
            // dd(request()->input());
        });
       
     }

    public function getTypeTextAttribute()
    {
        return static::$typeMap[$this->type] ?? '';
    }

    public function category()
    {
        return $this->belongsTo(ConfigCategory::class);
    }

    /**
     * 通过配置分类标识，获取所有配置
     *
     * @param string $categorySlug
     * @param bool $onlyValues
     *
     * @return Config[]|\Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection
     */
    public static function getByCategorySlug(string $categorySlug, bool $onlyValues = false)
    {
        $configs = static::whereHas('category', function (Builder $query) use ($categorySlug) {
            $query->where('slug', $categorySlug);
        })->orderBy('order')->get();

        if ($onlyValues) {
            return $configs->pluck('value', 'slug');
        } else {
            return $configs;
        }
    }

    /**
     * @param Config[]|\Illuminate\Support\Collection $configs
     * @param array $inputs slug => value 键值对
     *
     * @return Config[]|\Illuminate\Support\Collection
     */
    public static function updateValues($configs, $inputs)
    {
        $configs->each(function (Config $config) use ($inputs) {
            if (key_exists($config->slug, $inputs)) {
                $config->update(['value' => $inputs[$config->slug]]);
            }
        });

        return $configs->pluck('value', 'slug');
    }

    public static function getConfigValue(string $slug, $default = null)
    {
        return static::query()->where('slug', $slug)->value('value') ?? $default;
    }

    protected static function getConfigGroupsFromDB(): array
    {
        // 避免刚安装系统时，还没有数据库的情况
        try {
            $groups = ConfigCategory::query()
                ->select(['id', 'slug'])
                ->with('configs:category_id,slug,value')
                ->get()
                ->map(function (ConfigCategory $category) {
                    return [
                        'slug' => $category->slug,
                        'configs' => $category->configs->pluck('value', 'slug')->toArray(),
                    ];
                })
                ->pluck('configs', 'slug')
                ->toArray();
        } catch (Throwable $e) {
            report($e);
            $groups = [];
        }

        return $groups;
    }

    /**
     * 如果直接用 config 函数获取配置，则已经是服务提供者里被替换了的
     * 这里要获取原始的文件配置
     *
     * @return array
     */
    protected static function getConfigGroupsFromFile(): array
    {
        $configFilePath = app('path.config').'/'.static::CONFIG_KEY.'.php';

        $config = [];
        if (file_exists($configFilePath) && !is_array($config = include $configFilePath)) {
            $config = [];
        }

        return $config;
    }

    public static function getDottedConfigFromCache(): array
    {
        return Cache::rememberForever(static::CACHE_KEY, function () {
            return array_merge(
                static::dotConfigs(static::getConfigGroupsFromFile()),
                static::dotConfigs(static::getConfigGroupsFromDB())
            );
        });
    }

    protected static function dotConfigs(array $configs)
    {
        return Arr::dot($configs, static::CONFIG_KEY.'.');
    }

    public static function loadToConfig()
    {
        config(static::getDottedConfigFromCache());
    }

    public static function clearConfigCache()
    {
        Cache::forget(static::CACHE_KEY);
    }

    /**
     * 验证规则排序
     * @param Array $validationRuleMap
     */
    public static function validationRuleMapSort($validationRuleMap){
        foreach ($validationRuleMap as $key => $value) {
            $validationRuleMap[$key]['count'] = self::query()
                ->where('validation_rules', 'like', "%{$value['key']}%")
                ->count();
        }
        $validationRuleMap = array_key_sort($validationRuleMap, 'count', SORT_DESC);
        return $validationRuleMap;
    }

    /**
     * 字段类型排序
     * @param Array $typeMap
     */
    public static function typeMapSort($typeMap){
        $typeGroup = self::query()
            ->select(DB::raw("count(*) as count, type"))
            ->whereNotNull('type')
            ->orderByDesc('count')
            ->groupBy('type')
            ->get()
            ->toArray();
        $typeGroup = array_column($typeGroup, 'type');
        return array_merge(orderByArray($typeMap, $typeGroup ,true), $typeMap);
    }

    /**
     * 上个/下个配置
     * @param Int $category_id
     * @param Int $id
     */
    public function prevNextSlug($category_id,$id){
        $re['prev_config'] = $this->where('category_id', $category_id)
            ->where('id', '<', $id)
            ->orderByDesc('id')
            ->first(['id','name']);
        $re['next_config'] = $this->where('category_id', $category_id)
            ->where('id', '>', $id)
            ->orderBy('id')
            ->first(['id','name']);
        return $re; 
    }

    public function formatValue($value)
    {
        $values = array_filter(explode('#', $value));
        return str_replace("\n", "<br/>", implode($values));
    }

    /**
    * 所有配置
    * @param Int $parent_id
    * @param Int $id
    */
    public function configs($category_id){
        return $this->where('category_id', $category_id)
            ->orderBy('id')
            ->get(['id','slug','name']);
    }
}
